;连接器的表示
;value是局部状态变量，informant是设置连接器值的对象，constraints是这一连接器涉及的所有约束的表
(define (make-connector)
    (let ((value #f) (informant #f) (constraints '()))
        (define (set-my-value newval setter)
            (cond ((not (has-value? me))
                   (set! value newval)
                   (set! informant setter)
                   (for-each-except setter
                                    inform-about-value
                                    constraints))
                  ((not (= value newval))
                   (error "Contradiction" (list value newval)))
                  (else 'ignored)))
        (define (forget-my-value retractor)
            (if (eq? retractor informant)
                (begin (set! informant #f)
                       (for-each-except retractor
                                        inform-about-no-value
                                        constraints))
                'ignored))
        (define (connect new-constraint)
            (if (not (memq new-constraint constraints))
                (set! constraints
                      (cons new-constraint constraints)))
            (if (has-value? me)
                (inform-about-value new-constraint))
            'done)
        (define (me request)
            (cond ((eq? request 'has-value?)
                   (if informant #t #f))
                  ((eq? request 'value) value)
                  ((eq? request 'set-value!) set-my-value)
                  ((eq? request 'forget) forget-my-value)
                  ((eq? request 'connect) connect)
                  (else (error "Unknown operation -- CONNECTOR" request))))
        me))

;将一个指定的过程应用于一个表中除了给定的特例之外的所有对象
(define (for-each-except exception procedure list)
    (define (loop items)
        (cond ((null? items) 'done)
              ((eq? (car items) exception) (loop (cdr items)))
              (else (procedure (car items))
                    (loop (cdr items)))))
    (loop list))

;连接器是否有值
(define (has-value? connector)
    (connector 'has-value?))

;返回连接器的值
(define (get-value connector)
    (connector 'value))

;信息源(informant)连接器的值设置为新值
(define (set-value! connector new-value informant)
    ((connector 'set-value!) new-value informant))

;撤销源(retractor)要求连接器忘记其值
(define (forget-value! connector retractor)
    ((connector 'forget) retractor))

;通知连接器参与一个新约束
(define (connect connector new-constraint)
    ((connector 'connect) new-constraint))

;与各个相关约束通信，告知给定的约束，现在该连接器有了一个新值
(define (inform-about-value constraint)
    (constraint 'I-have-a-value))

;告知相关约束，该连接器丧失了自己原有的值
(define (inform-about-no-value constraint)
    (constraint 'I-lost-my-value))

;在被求和连接器a1和a2与和连接器sum之间构造出一个加法约束
(define (adder a1 a2 sum)
    (define (process-new-value)
        (cond ((and (has-value? a1) (has-value? a2))
               (set-value! sum
                           (+ (get-value a1) (get-value a2))
                           me))
              ((and (has-value? a1) (has-value? sum))
               (set-value! a2
                           (- (get-value sum) (get-value a1))
                           me))
              ((and (has-value? a2) (has-value? sum))
               (set-value! a1
                           (- (get-value sum) (get-value a2))
                           me))))
    (define (process-forget-value)
        (forget-value! sum me)
        (forget-value! a1 me)
        (forget-value! a2 me)
        (process-new-value))
    (define (me request)
        (cond ((eq? request 'I-have-a-value)
               (process-new-value))
              ((eq? request 'I-lost-my-value)
               (process-forget-value))
              (else
                (error "Unknown request -- ADDER" request))))
    (connect a1 me)
    (connect a2 me)
    (connect sum me)
    me)

;乘法约束
(define (multiplier m1 m2 product)
    (define (process-new-value)
        (cond ((or (and (has-value? m1) (= (get-value m1) 0))
                   (and (has-value? m2) (= (get-value m2) 0)))
                (set-value! product 0 me))
              ((and (has-value? m1) (has-value? m2))
               (set-value! product
                           (* (get-value m1) (get-value m2))
                           me))
              ((and (has-value? product) (has-value? m1))
               (set-value! m2
                           (/ (get-value product) (get-value m1))
                           me))
              ((and (has-value? product) (has-value? m2))
               (set-value! m1
                           (/ (get-value product) (get-value m2))
                           me))))
    (define (process-forget-value)
        (forget-value! product me)
        (forget-value! m1 me)
        (forget-value! m2 me)
        (process-new-value))
    (define (me request)
        (cond ((eq? request 'I-have-a-value)
               (process-new-value))
              ((eq? request 'I-lost-my-value)
               (process-forget-value))
              (else
                (error "Unknown request -- MULTIPLIER" request))))
    (connect m1 me)
    (connect m2 me)
    (connect product me)
    me)

;常量约束，让连接器成为一个常量，不允许更改
(define (constant value connector)
    (define (me request)
        (error "Unkonwn request -- CONSTANT" request))
    (connect connector me)
    (set-value! connector value me)
    me)

;监视器，在指定连接器被设置或取消值的时候打印一个消息
(define (probe name connector)
    (define (print-probe value)
        (newline)
        (display "Probe: ")
        (display name)
        (display " = ")
        (display value))
    (define (process-new-value)
        (print-probe (get-value connector)))
    (define (process-forget-value)
        (print-probe "?"))
    (define (me request)
        (cond ((eq? request 'I-have-a-value)
               (process-new-value))
              ((eq? request 'I-lost-my-value)
               (process-forget-value))
              (else
                (error "Unkonwn request -- PROBE" request))))
    (connect connector me)
    me)
;---------------------------------------------------------------
;约束系统的使用

;一个华氏温度和摄氏温度的约束网络
(define (celsius-fahrenheit-converter c f)
    (let ((u (make-connector))
          (v (make-connector))
          (w (make-connector))
          (x (make-connector))
          (y (make-connector)))
        (multiplier c w u)
        (multiplier v x u)
        (adder v y f)
        (constant 9 w)
        (constant 5 x)
        (constant 32 y)
        'ok))

;测试
;(define C (make-connector))
;(define F (make-connector))
;(celsius-fahrenheit-converter C F)

;安装监视器
;(probe "Celsius temp" C)
;(probe "Fahrenheit temp" F)

;(set-value! C 25 'user)
;(set-value! F 212 'user) ;==> Exception in error: invalid message argument (77 212)

;(forget-value! C 'user)
;(set-value! F 212 'user)